home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / MDB / oci8.php < prev    next >
PHP Script  |  2004-03-24  |  67KB  |  1,711 lines

  1. <?php 
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  6. // | Stig. S. Bakken, Lukas Smith                                         |
  7. // | All rights reserved.                                                 |
  8. // +----------------------------------------------------------------------+
  9. // | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
  10. // | API as well as database abstraction for PHP applications.            |
  11. // | This LICENSE is in the BSD license style.                            |
  12. // |                                                                      |
  13. // | Redistribution and use in source and binary forms, with or without   |
  14. // | modification, are permitted provided that the following conditions   |
  15. // | are met:                                                             |
  16. // |                                                                      |
  17. // | Redistributions of source code must retain the above copyright       |
  18. // | notice, this list of conditions and the following disclaimer.        |
  19. // |                                                                      |
  20. // | Redistributions in binary form must reproduce the above copyright    |
  21. // | notice, this list of conditions and the following disclaimer in the  |
  22. // | documentation and/or other materials provided with the distribution. |
  23. // |                                                                      |
  24. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  25. // | Lukas Smith nor the names of his contributors may be used to endorse |
  26. // | or promote products derived from this software without specific prior|
  27. // | written permission.                                                  |
  28. // |                                                                      |
  29. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  30. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  31. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  32. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  33. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  34. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  37. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  38. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  40. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  41. // +----------------------------------------------------------------------+
  42. // | Author: Lukas Smith <smith@backendmedia.com>                         |
  43. // +----------------------------------------------------------------------+
  44.  
  45. // $Id: oci8.php,v 1.21.4.9 2004/01/08 13:43:02 lsmith Exp $
  46.  
  47. require_once('MDB/Common.php');
  48.  
  49. /**
  50.  * MDB OCI8 driver
  51.  *
  52.  * Notes:
  53.  * - when fetching in associative mode all keys are uppercased which is not the
  54.  *   intenteded behavior. Due to BC issues this will not be changed in MDB 1.x
  55.  *   however.
  56.  *
  57.  * - createDatabase and dropDatabase are not supported
  58.  *
  59.  * - Text fields with unspecified length limit are created as VARCHAR with an
  60.  *   optional limit that may not exceed 4000 characters.
  61.  *
  62.  * - date fields are emulated with date fields with time set to 00:00:00.
  63.      time fields are emulated with date fields with the day set to 0001-01-01.
  64.  *
  65.  * - The numRows method is emulated by fetching all the rows into memory.
  66.  *   Avoid using it if for queries with large result sets.
  67.  *
  68.  * - Oracle does not provide direct support for returning result sets restricted
  69.      to a given range. Such support is emulated in the MDB oci8 driver.
  70.  *
  71.  * - Storing data in large object fields has to be done in two phases: first the
  72.      fields are initialized using a INSERT or UPDATE query that sets the fields
  73.      to an empty value, then the data values are uploaded to the large objects
  74.      returned by reference from the executed queries.
  75.  *   Besides the fact that only INSERT and UPDATE queries are supported to
  76.      upload large object data values, only UPDATE queries that affect only one
  77.      row will set the large object fields correctly.
  78.  *
  79.  * - The driver alterTable method does not implement table or column renaming.
  80.  *
  81.  * @package MDB
  82.  * @category Database
  83.  * @author Lukas Smith <smith@backendmedia.com> 
  84.  */
  85. class MDB_oci8 extends MDB_Common {
  86.     var $connection = 0;
  87.     var $connected_user;
  88.     var $connected_password;
  89.  
  90.     var $escape_quotes = "'";
  91.  
  92.     var $uncommitedqueries = 0;
  93.  
  94.     var $results = array();
  95.     var $current_row = array();
  96.     var $columns = array();
  97.     var $rows = array();
  98.     var $limits = array();
  99.     var $row_buffer = array();
  100.     var $highest_fetched_row = array();
  101.  
  102.     // {{{ constructor
  103.  
  104.     /**
  105.      * Constructor
  106.      */
  107.     function MDB_oci8()
  108.     {
  109.         $this->MDB_Common();
  110.         $this->phptype = 'oci8';
  111.         $this->dbsyntax = 'oci8';
  112.         
  113.         $this->supported['Sequences'] = 1;
  114.         $this->supported['Indexes'] = 1;
  115.         $this->supported['SummaryFunctions'] = 1;
  116.         $this->supported['OrderByText'] = 1;
  117.         $this->supported["AffectedRows"]= 1;
  118.         $this->supported['Transactions'] = 1;
  119.         $this->supported['SelectRowRanges'] = 1;
  120.         $this->supported['LOBs'] = 1;
  121.         $this->supported['Replace'] = 1;
  122.         $this->supported['SubSelects'] = 1;
  123.         
  124.         $this->options['DBAUser'] = FALSE;
  125.         $this->options['DBAPassword'] = FALSE;
  126.         
  127.         $this->errorcode_map = array(
  128.             900 => MDB_ERROR_SYNTAX,
  129.             904 => MDB_ERROR_NOSUCHFIELD,
  130.             923 => MDB_ERROR_SYNTAX,
  131.             942 => MDB_ERROR_NOSUCHTABLE,
  132.             2289 => MDB_ERROR_NOSUCHTABLE,
  133.             955 => MDB_ERROR_ALREADY_EXISTS,
  134.             1476 => MDB_ERROR_DIVZERO,
  135.             1722 => MDB_ERROR_INVALID_NUMBER,
  136.             2289 => MDB_ERROR_NOSUCHTABLE,
  137.             2291 => MDB_ERROR_CONSTRAINT,
  138.             2449 => MDB_ERROR_CONSTRAINT,
  139.         );
  140.     }
  141.  
  142.     // }}}
  143.     // {{{ errorNative()
  144.  
  145.     /**
  146.      * Get the native error code of the last error (if any) that
  147.      * occured on the current connection.
  148.      * 
  149.      * @access public 
  150.      * @return int native oci8 error code
  151.      */
  152.     function errorNative($statement = NULL)
  153.     {
  154.         if (is_resource($statement)) {
  155.             $error = @OCIError($statement);
  156.         } else {
  157.             $error = @OCIError($this->connection);
  158.         }
  159.         if (is_array($error)) {
  160.             return($error['code']);
  161.         }
  162.         return(FALSE);
  163.     }
  164.  
  165.     // }}}
  166.     // {{{ oci8RaiseError()
  167.  
  168.     /**
  169.      * This method is used to communicate an error and invoke error
  170.      * callbacks etc.  Basically a wrapper for MDB::raiseError
  171.      * that checks for native error msgs.
  172.      * 
  173.      * @param integer $errno error code
  174.      * @return object a PEAR error object
  175.      * @access public 
  176.      * @see PEAR_Error
  177.      */
  178.     function oci8RaiseError($errno = NULL, $message = NULL)
  179.     {
  180.         if ($errno === NULL) {
  181.             $error = @OCIError($this->connection);
  182.             return($this->raiseError($this->errorCode($error['code']),
  183.                 NULL, NULL, NULL, $error['message']));
  184.         } elseif (is_resource($errno)) {
  185.             $error = @OCIError($errno);
  186.             return($this->raiseError($this->errorCode($error['code']),
  187.                 NULL, NULL, NULL, $error['message']));
  188.         }
  189.         return($this->raiseError($this->errorCode($errno), NULL, NULL, $message));
  190.     }
  191.  
  192.     // }}}
  193.     // {{{ autoCommit()
  194.  
  195.     /**
  196.      * Define whether database changes done on the database be automatically
  197.      * committed. This function may also implicitly start or end a transaction.
  198.      * 
  199.      * @param boolean $auto_commit flag that indicates whether the database
  200.      *                                 changes should be committed right after
  201.      *                                 executing every query statement. If this
  202.      *                                 argument is 0 a transaction implicitly
  203.      *                                 started. Otherwise, if a transaction is
  204.      *                                 in progress it is ended by committing any
  205.      *                                 database changes that were pending.
  206.      * @access public 
  207.      * @return mixed MDB_OK on success, a MDB error on failure
  208.      */
  209.     function autoCommit($auto_commit)
  210.     {
  211.         $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off'));
  212.         if ($this->auto_commit == $auto_commit) {
  213.             return(MDB_OK);
  214.         }
  215.         if ($this->connection && $auto_commit && MDB::isError($commit = $this->commit())) {
  216.             return($commit);
  217.         }
  218.         $this->auto_commit = $auto_commit;
  219.         $this->in_transaction = !$auto_commit;
  220.         return(MDB_OK);
  221.     }
  222.  
  223.     // }}}
  224.     // {{{ commit()
  225.  
  226.     /**
  227.      * Commit the database changes done during a transaction that is in
  228.      * progress. This function may only be called when auto-committing is
  229.      * disabled, otherwise it will fail. Therefore, a new transaction is
  230.      * implicitly started after committing the pending changes.
  231.      * 
  232.      * @access public 
  233.      * @return mixed MDB_OK on success, a MDB error on failure
  234.      */
  235.     function commit()
  236.     {
  237.         $this->debug('Commit Transaction');
  238.         if (!isset($this->supported['Transactions'])) {
  239.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
  240.                 'Commit transactions: transactions are not in use'));
  241.         }
  242.         if ($this->auto_commit) {
  243.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  244.             'Commit transactions: transaction changes are being auto commited'));
  245.         }
  246.         if ($this->uncommitedqueries) {
  247.             if (!@OCICommit($this->connection)) {
  248.                 return($this->oci8RaiseError(NULL,
  249.                     'Commit transactions: Could not commit pending transaction: '."$message. Error: ".$error['code'].' ('.$error['message'].')'));
  250.             }
  251.             $this->uncommitedqueries = 0;
  252.         }
  253.         return(MDB_OK);
  254.     }
  255.  
  256.     // }}}
  257.     // {{{ rollback()
  258.  
  259.     /**
  260.      * Cancel any database changes done during a transaction that is in
  261.      * progress. This function may only be called when auto-committing is
  262.      * disabled, otherwise it will fail. Therefore, a new transaction is
  263.      * implicitly started after canceling the pending changes.
  264.      * 
  265.      * @access public 
  266.      * @return mixed MDB_OK on success, a MDB error on failure
  267.      */
  268.     function rollback()
  269.     {
  270.         $this->debug('Rollback Transaction');
  271.         if ($this->auto_commit) {
  272.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  273.                 'Rollback transactions: transactions can not be rolled back when changes are auto commited'));
  274.         }
  275.         if ($this->uncommitedqueries) {
  276.             if (!@OCIRollback($this->connection)) {
  277.                 return($this->oci8RaiseError(NULL,
  278.                     'Rollback transaction: Could not rollback pending transaction'));
  279.             }
  280.             $this->uncommitedqueries = 0;
  281.         }
  282.         return(MDB_OK);
  283.     }
  284.  
  285.     // }}}
  286.     // {{{ connect()
  287.  
  288.     /**
  289.      * Connect to the database
  290.      * 
  291.      * @return TRUE on success, MDB_Error on failure
  292.      */
  293.     function connect($user = NULL , $password = NULL, $persistent = NULL)
  294.     {
  295.         if($user === NULL) {
  296.             $user = $this->user;
  297.         }
  298.         if($password === NULL) {
  299.             $password = $this->password;
  300.         }
  301.         if($persistent === NULL) {
  302.             $persistent = $this->getOption('persistent');
  303.         }
  304.         if (isset($this->host)) {
  305.             $sid = $this->host;
  306.         } else {
  307.             $sid = getenv('ORACLE_SID');
  308.         }
  309.         if(!strcmp($sid, '')) {
  310.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  311.                 'Connect: it was not specified a valid Oracle Service IDentifier (SID)'));
  312.         }
  313.         if($this->connection != 0) {
  314.             if (!strcmp($this->connected_user, $user)
  315.                 && !strcmp($this->connected_password, $password)
  316.                 && $this->opened_persistent == $persistent)
  317.             {
  318.                 return(MDB_OK);
  319.             }
  320.             $this->_close();
  321.         }
  322.  
  323.         if(!PEAR::loadExtension($this->phptype)) {
  324.             return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
  325.                 NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
  326.                 'MDB_Error', TRUE));
  327.         }
  328.  
  329.         if (isset($this->options['HOME'])) {
  330.             putenv('ORACLE_HOME='.$this->options['HOME']);
  331.         }
  332.         putenv('ORACLE_SID='.$sid);
  333.         $function = ($persistent ? 'OCIPLogon' : 'OCINLogon');
  334.         if (!function_exists($function)) {
  335.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  336.                 'Connect: Oracle OCI API support is not available in this PHP configuration'));
  337.         }
  338.         if (!($this->connection = @$function($user, $password, $sid))) {
  339.             return($this->oci8RaiseError(NULL,
  340.                 'Connect: Could not connect to Oracle server'));
  341.         }
  342.         if (MDB::isError($doquery = $this->_doQuery("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"))) {
  343.             $this->_close();
  344.             return($doquery);
  345.         }
  346.         if (MDB::isError($doquery = $this->_doQuery("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='. '"))) {
  347.             $this->_close();
  348.             return($doquery);
  349.         }
  350.  
  351.         $this->connected_user = $user;
  352.         $this->connected_password = $password;
  353.         $this->opened_persistent = $persistent;
  354.         return(MDB_OK);
  355.     }
  356.  
  357.     // }}}
  358.     // {{{ _close()
  359.  
  360.     /**
  361.      * all the RDBMS specific things needed close a DB connection
  362.      * 
  363.      * @access private 
  364.      */
  365.     function _close()
  366.     {
  367.         if ($this->connection != 0) {
  368.             @OCILogOff($this->connection);
  369.             $this->connection = 0;
  370.             $this->affected_rows = -1;
  371.             $this->uncommitedqueries = 0;
  372.         }
  373.     }
  374.  
  375.     // }}}
  376.     // {{{ _doQuery()
  377.  
  378.     /**
  379.      * all the RDBMS specific things needed close a DB connection
  380.      * 
  381.      * @access private
  382.      */
  383.     function _doQuery($query, $first = 0, $limit = 0, $prepared_query = 0)
  384.     {
  385.         $lobs = 0;
  386.         $success = MDB_OK;
  387.         $result = 0;
  388.         $descriptors = array();
  389.         if ($prepared_query) {
  390.             $columns = '';
  391.             $variables = '';
  392.             for(reset($this->clobs[$prepared_query]), $clob = 0;
  393.                 $clob < count($this->clobs[$prepared_query]);
  394.                 $clob++, next($this->clobs[$prepared_query]))
  395.             {
  396.                 $position = key($this->clobs[$prepared_query]);
  397.                 if (gettype($descriptors[$position] = @OCINewDescriptor($this->connection, OCI_D_LOB)) != 'object') {
  398.                     $success = $this->raiseError(MDB_ERROR, NULL, NULL,
  399.                         'Do query: Could not create descriptor for clob parameter');
  400.                     break;
  401.                 }
  402.                 $columns.= ($lobs == 0 ? ' RETURNING ' : ',').$this->prepared_queries[$prepared_query-1]['Fields'][$position-1];
  403.                 $variables.= ($lobs == 0 ? ' INTO ' : ',').':clob'.$position;
  404.                 $lobs++;
  405.             }
  406.             if (!MDB::isError($success)) {
  407.                 for(reset($this->blobs[$prepared_query]), $blob = 0;$blob < count($this->blobs[$prepared_query]);$blob++, next($this->blobs[$prepared_query])) {
  408.                     $position = key($this->blobs[$prepared_query]);
  409.                     if (gettype($descriptors[$position] = @OCINewDescriptor($this->connection, OCI_D_LOB)) != 'object') {
  410.                         $success = $this->raiseError(MDB_ERROR, NULL, NULL,
  411.                             'Do query: Could not create descriptor for blob parameter');
  412.                         break;
  413.                     }
  414.                     $columns.= ($lobs == 0 ? ' RETURNING ' : ',').$this->prepared_queries[$prepared_query-1]['Fields'][$position-1];
  415.                     $variables.= ($lobs == 0 ? ' INTO ' : ',').':blob'.$position;
  416.                     $lobs++;
  417.                 }
  418.                 $query.= $columns.$variables;
  419.             }
  420.         }
  421.         if (!MDB::isError($success)) {
  422.             if (($statement = @OCIParse($this->connection, $query))) {
  423.                 if ($lobs) {
  424.                     for(reset($this->clobs[$prepared_query]), $clob = 0;$clob < count($this->clobs[$prepared_query]);$clob++, next($this->clobs[$prepared_query])) {
  425.                         $position = key($this->clobs[$prepared_query]);
  426.                         if (!@OCIBindByName($statement, ':clob'.$position, $descriptors[$position], -1, OCI_B_CLOB)) {
  427.                             $success = $this->oci8RaiseError(NULL,
  428.                                 'Do query: Could not bind clob upload descriptor');
  429.                             break;
  430.                         }
  431.                     }
  432.                     if (!MDB::isError($success)) {
  433.                         for(reset($this->blobs[$prepared_query]), $blob = 0;
  434.                             $blob < count($this->blobs[$prepared_query]);
  435.                             $blob++, next($this->blobs[$prepared_query]))
  436.                         {
  437.                             $position = key($this->blobs[$prepared_query]);
  438.                             if (!@OCIBindByName($statement, ':blob'.$position, $descriptors[$position], -1, OCI_B_BLOB)) {
  439.                                 $success = $this->oci8RaiseError(NULL,
  440.                                     'Do query: Could not bind blob upload descriptor');
  441.                                 break;
  442.                             }
  443.                         }
  444.                     }
  445.                 }
  446.                 if (!MDB::isError($success)) {
  447.                     if (($result = @OCIExecute($statement, ($lobs == 0 && $this->auto_commit) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT))) {
  448.                         if ($lobs) {
  449.                             for(reset($this->clobs[$prepared_query]), $clob = 0;
  450.                                 $clob < count($this->clobs[$prepared_query]);
  451.                                 $clob++, next($this->clobs[$prepared_query]))
  452.                             {
  453.                                 $position = key($this->clobs[$prepared_query]);
  454.                                 $clob_stream = $this->prepared_queries[$prepared_query-1]['Values'][$position-1];
  455.                                 for($value = '';!$this->endOfLOB($clob_stream);) {
  456.                                     if ($this->readLOB($clob_stream, $data, $this->getOption('lob_buffer_length')) < 0) {
  457.                                         $success = $this->raiseError();
  458.                                         break;
  459.                                     }
  460.                                     $value.= $data;
  461.                                 }
  462.                                 if (!MDB::isError($success) && !$descriptors[$position]->save($value)) {
  463.                                     $success = $this->oci8RaiseError(NULL,
  464.                                         'Do query: Could not upload clob data');
  465.                                 }
  466.                             }
  467.                             if (!MDB::isError($success)) {
  468.                                 for(reset($this->blobs[$prepared_query]), $blob = 0;$blob < count($this->blobs[$prepared_query]);$blob++, next($this->blobs[$prepared_query])) {
  469.                                     $position = key($this->blobs[$prepared_query]);
  470.                                     $blob_stream = $this->prepared_queries[$prepared_query-1]['Values'][$position-1];
  471.                                     for($value = '';!$this->endOfLOB($blob_stream);) {
  472.                                         if ($this->readLOB($blob_stream, $data, $this->getOption('lob_buffer_length')) < 0) {
  473.                                             $success = $this->raiseError();
  474.                                             break;
  475.                                         }
  476.                                         $value.= $data;
  477.                                     }
  478.                                     if (!MDB::isError($success) && !$descriptors[$position]->save($value)) {
  479.                                         $success = $this->oci8RaiseError(NULL,
  480.                                                 'Do query: Could not upload blob data');
  481.                                     }
  482.                                 }
  483.                             }
  484.                         }
  485.                         if ($this->auto_commit) {
  486.                             if ($lobs) {
  487.                                 if (MDB::isError($success)) {
  488.                                     if (!@OCIRollback($this->connection)) {
  489.                                         $success = $this->oci8RaiseError(NULL,
  490.                                             'Do query: '.$success->getUserinfo().' and then could not rollback LOB updating transaction');
  491.                                     }
  492.                                 } else {
  493.                                     if (!@OCICommit($this->connection)) {
  494.                                         $success = $this->oci8RaiseError(NULL,
  495.                                             'Do query: Could not commit pending LOB updating transaction');
  496.                                     }
  497.                                 }
  498.                             }
  499.                         } else {
  500.                             $this->uncommitedqueries++;
  501.                         }
  502.                         if (!MDB::isError($success)) {
  503.                             switch (@OCIStatementType($statement)) {
  504.                                 case 'SELECT':
  505.                                     $result_value = intval($statement);
  506.                                     $this->current_row[$result_value] = -1;
  507.                                     if ($limit > 0) {
  508.                                         $this->limits[$result_value] = array($first, $limit, 0);
  509.                                     }
  510.                                     $this->highest_fetched_row[$result_value] = -1;
  511.                                     break;
  512.                                 default:
  513.                                     $this->affected_rows = @OCIRowCount($statement);
  514.                                     @OCIFreeCursor($statement);
  515.                                     break;
  516.                             }
  517.                             $result = $statement;
  518.                         }
  519.                     } else {
  520.                         return($this->oci8RaiseError($statement, 'Do query: Could not execute query'));
  521.                     }
  522.                 }
  523.             } else {
  524.                 return($this->oci8RaiseError(NULL, 'Do query: Could not parse query'));
  525.             }
  526.         }
  527.         for(reset($descriptors), $descriptor = 0;
  528.             $descriptor < count($descriptors);
  529.             $descriptor++, next($descriptors))
  530.         {
  531.             @OCIFreeDesc($descriptors[key($descriptors)]);
  532.         }
  533.         return($result);
  534.     }
  535.  
  536.     // }}}
  537.     // {{{ query()
  538.  
  539.    /**
  540.      * Send a query to the database and return any results
  541.      * 
  542.      * @access public 
  543.      * @param string $query the SQL query
  544.      * @param array $types array that contains the types of the columns in
  545.      *                         the result set
  546.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  547.      */
  548.     function query($query, $types = NULL)
  549.     {
  550.         $this->debug("Query: $query");
  551.         $this->last_query = $query;
  552.         $first = $this->first_selected_row;
  553.         $limit = $this->selected_row_limit;
  554.         $this->first_selected_row = $this->selected_row_limit = 0;
  555.         if (MDB::isError($connect = $this->connect())) {
  556.             return($connect);
  557.         }
  558.         if(!MDB::isError($result = $this->_doQuery($query, $first, $limit))) {
  559.             if ($types != NULL) {
  560.                 if (!is_array($types)) {
  561.                     $types = array($types);
  562.                 }
  563.                 if (MDB::isError($err = $this->setResultTypes($result, $types))) {
  564.                     $this->freeResult($result);
  565.                     return($err);
  566.                 }
  567.             }
  568.             return($result);
  569.         }
  570.         return($this->oci8RaiseError());
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ _executePreparedQuery()
  575.  
  576.     /**
  577.      * Execute a prepared query statement.
  578.      *
  579.      * @param int $prepared_query argument is a handle that was returned by
  580.      *       the function prepareQuery()
  581.      * @param string $query query to be executed
  582.      * @param array $types array that contains the types of the columns in
  583.      *       the result set
  584.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  585.      * @access private
  586.      */
  587.     function _executePreparedQuery($prepared_query, $query)
  588.     {
  589.         $first = $this->first_selected_row;
  590.         $limit = $this->selected_row_limit;
  591.         $this->first_selected_row = $this->selected_row_limit = 0;
  592.         if (MDB::isError($connect = $this->connect())) {
  593.             return($connect);
  594.         }
  595.         return($this->_doQuery($query, $first, $limit, $prepared_query));
  596.     }
  597.  
  598.     // }}}
  599.     // {{{ _skipLimitOffset()
  600.  
  601.     /**
  602.      * Skip the first row of a result set.
  603.      *
  604.      * @param resource $result
  605.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  606.      * @access private
  607.      */
  608.     function _skipLimitOffset($result)
  609.     {
  610.         $result_value = intval($result);
  611.         $first = $this->limits[$result_value][0];
  612.         for(;$this->limits[$result_value][2] < $first;$this->limits[$result_value][2]++) {
  613.             if (!@OCIFetch($result)) {
  614.                 $this->limits[$result_value][2] = $first;
  615.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  616.                     'Skip first rows: could not skip a query result row'));
  617.             }
  618.         }
  619.         return(MDB_OK);
  620.     }
  621.  
  622.     // }}}
  623.     // {{{ _getColumn()
  624.  
  625.     /**
  626.      * Get key for a given field with a result set.
  627.      *
  628.      * @param resource $result
  629.      * @param mixed $field integer or string key for the column
  630.      * @return mixed column from the result handle or a MDB error on failure
  631.      * @access private
  632.      */
  633.     function _getColumn($result, $field)
  634.     {
  635.         $result_value = intval($result);
  636.         if (MDB::isError($names = $this->getColumnNames($result))) {
  637.             return($names);
  638.         }
  639.          if (is_numeric($field)) {
  640.             if (($column = $field) < 0
  641.                 || $column >= count($this->columns[$result_value]))
  642.             {
  643.                 return($this->raiseError('Get column: attempted to fetch an query result column out of range'));
  644.             }
  645.         } else {
  646.             $name = strtolower($field);
  647.             if (!isset($this->columns[$result_value][$name])) {
  648.                 return($this->raiseError('Get column: attempted to fetch an unknown query result column'));
  649.             }
  650.             $column = $this->columns[$result_value][$name];
  651.         }
  652.         return($column);
  653.     }
  654.  
  655.     // }}}
  656.     // {{{ getColumnNames()
  657.  
  658.     /**
  659.      * Retrieve the names of columns returned by the DBMS in a query result.
  660.      * 
  661.      * @param resource $result result identifier
  662.      * @return mixed an associative array variable
  663.      *                               that will hold the names of columns.The
  664.      *                               indexes of the array are the column names
  665.      *                               mapped to lower case and the values are the
  666.      *                               respective numbers of the columns starting
  667.      *                               from 0. Some DBMS may not return any
  668.      *                               columns when the result set does not
  669.      *                               contain any rows.
  670.      * 
  671.      *                               a MDB error on failure
  672.      * @access public 
  673.      */
  674.     function getColumnNames($result)
  675.     {
  676.         $result_value = intval($result);
  677.         if (!isset($this->highest_fetched_row[$result_value])) {
  678.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  679.                 'Get column names: it was specified an inexisting result set'));
  680.         }
  681.         if (!isset($this->columns[$result_value])) {
  682.             $this->columns[$result_value] = array();
  683.             $columns = @OCINumCols($result);
  684.             for($column = 0; $column < $columns; $column++) {
  685.                 $this->columns[$result_value][strtolower(@OCIColumnName($result, $column + 1))] = $column;
  686.             }
  687.         }
  688.         return($this->columns[$result_value]);
  689.     }
  690.  
  691.     // }}}
  692.     // {{{ numCols()
  693.  
  694.     /**
  695.      * Count the number of columns returned by the DBMS in a query result.
  696.      * 
  697.      * @param resource $result result identifier
  698.      * @return mixed integer value with the number of columns, a MDB error
  699.      *      on failure
  700.      * @access public
  701.      */
  702.     function numCols($result)
  703.     {
  704.         if (!isset($this->highest_fetched_row[intval($result)])) {
  705.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  706.                 'Number of columns: it was specified an inexisting result set'));
  707.         }
  708.         return(@OCINumCols($result));
  709.     }
  710.  
  711.     // }}}
  712.     // {{{ endOfResult()
  713.  
  714.     /**
  715.      * check if the end of the result set has been reached
  716.      * 
  717.      * @param resource $result result identifier
  718.      * @return mixed TRUE or FALSE on sucess, a MDB error on failure
  719.      * @access public 
  720.      */
  721.     function endOfResult($result)
  722.     {
  723.         $result_value = intval($result);
  724.         if (!isset($this->current_row[$result_value])) {
  725.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  726.                 'End of result: attempted to check the end of an unknown result'));
  727.         }
  728.         if (isset($this->rows[$result_value])) {
  729.             return($this->highest_fetched_row[$result_value] >= $this->rows[$result_value]-1);
  730.         }
  731.         if (isset($this->row_buffer[$result_value])) {
  732.             return(FALSE);
  733.         }
  734.         if (isset($this->limits[$result_value])) {
  735.             if (MDB::isError($this->_skipLimitOffset($result))
  736.                 || $this->current_row[$result_value] + 1 >= $this->limits[$result_value][1]
  737.             ) {
  738.                 $this->rows[$result_value] = 0;
  739.                 return(TRUE);
  740.             }
  741.         }
  742.         if (@OCIFetchInto($result, $this->row_buffer[$result_value], OCI_ASSOC+OCI_RETURN_NULLS)) {
  743.             return(FALSE);
  744.         }
  745.         unset($this->row_buffer[$result_value]);
  746.         return(TRUE);
  747.     }
  748.  
  749.     // }}}
  750.     // {{{ _retrieveLob()
  751.  
  752.     /**
  753.      * fetch a lob value from a result set
  754.      * 
  755.      * @param int $lob handle to a lob created by the createLob() function
  756.      * @return mixed MDB_OK on success, a MDB error on failure
  757.      * @access private 
  758.      */
  759.     function _retrieveLob($lob)
  760.     {
  761.         if (!isset($this->lobs[$lob])) {
  762.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  763.                 'Retrieve LOB: it was not specified a valid lob'));
  764.         }
  765.         if (!isset($this->lobs[$lob]['Value'])) {
  766.             unset($lob_object);
  767.             $result = $this->lobs[$lob]['Result'];
  768.             $row = $this->lobs[$lob]['Row'];
  769.             $field = $this->lobs[$lob]['Field'];
  770.             $lob_object = $this->fetch($result, $row, $field);
  771.             if (gettype($lob_object) != 'object') {
  772.                 if (($column = $this->_getColumn($result, $field)) == -1) {
  773.                     return($column);
  774.                 }
  775.                 if (isset($this->results[intval($result)][$row][$column])) {
  776.                     return($this->raiseError(MDB_ERROR, NULL, NULL,
  777.                         'Retrieve LOB: attemped to retrive a non LOB result column'));
  778.                 } else {
  779.                     return($this->raiseError(MDB_ERROR, NULL, NULL,
  780.                         'Retrieve LOB: attemped to retrieve LOB from non existing or NULL column'));
  781.                }
  782.             }
  783.             $this->lobs[$lob]['Value'] = $lob_object->load();
  784.         }
  785.         return(MDB_OK);
  786.     }
  787.  
  788.     // }}}
  789.     // {{{ fetch()
  790.  
  791.     /**
  792.      * fetch value from a result set
  793.      * 
  794.      * @param resource $result result identifier
  795.      * @param int $rownum number of the row where the data can be found
  796.      * @param int $field field number where the data can be found
  797.      * @return mixed string on success, a MDB error on failure
  798.      * @access public 
  799.      */
  800.     function fetch($result, $rownum, $field)
  801.     {
  802.         if (MDB::isError($column = $this->_getColumn($result, $field))) {
  803.             return($column);
  804.         }
  805.         $row = $this->fetchInto($result, MDB_FETCHMODE_ORDERED, $rownum);
  806.         if (MDB::isError($row)) {
  807.             return($row);
  808.         }
  809.         if (!isset($row[$column])) {
  810.             return(NULL);
  811.         }
  812.         return($row[$column]);
  813.     }
  814.  
  815.     // }}}
  816.     // {{{ fetchClob()
  817.  
  818.     /**
  819.      * fetch a clob value from a result set
  820.      * 
  821.      * @param resource $result result identifier
  822.      * @param int $row number of the row where the data can be found
  823.      * @param int $field field number where the data can be found
  824.      * @return mixed content of the specified data cell, a MDB error on failure,
  825.      *                a MDB error on failure
  826.      * @access public 
  827.      */
  828.     function fetchClob($result, $row, $field)
  829.     {
  830.         return($this->fetchLOB($result, $row, $field));
  831.     }
  832.  
  833.     // }}}
  834.     // {{{ fetchBlob()
  835.     /**
  836.      * fetch a blob value from a result set
  837.      * 
  838.      * @param resource $result result identifier
  839.      * @param int $row number of the row where the data can be found
  840.      * @param int $field field number where the data can be found
  841.      * @return mixed content of the specified data cell, a MDB error on failure
  842.      * @access public 
  843.      */
  844.     function fetchBlob($result, $row, $field)
  845.     {
  846.         return($this->fetchLOB($result, $row, $field));
  847.     }
  848.  
  849.     // }}}
  850.     // {{{ resultIsNull()
  851.  
  852.     /**
  853.      * Determine whether the value of a query result located in given row and
  854.      *    field is a NULL.
  855.      * 
  856.      * @param resource $result result identifier
  857.      * @param int $rownum number of the row where the data can be found
  858.      * @param int $field field number where the data can be found
  859.      * @return mixed TRUE or FALSE on success, a MDB error on failure
  860.      * @access public 
  861.      */
  862.     function resultIsNull($result, $rownum, $field)
  863.     {
  864.         $value = $this->fetch($result, $rownum, $field);
  865.         if (MDB::isError($value)) {
  866.             return($value);
  867.         }
  868.         return(!isset($value));
  869.     }
  870.  
  871.     // }}}
  872.     // {{{ convertResult()
  873.  
  874.     /**
  875.      * convert a value to a RDBMS indepdenant MDB type
  876.      * 
  877.      * @param mixed $value value to be converted
  878.      * @param int $type constant that specifies which type to convert to
  879.      * @return mixed converted value
  880.      * @access public 
  881.      */
  882.     function convertResult($value, $type)
  883.     {
  884.         switch ($type) {
  885.             case MDB_TYPE_DATE:
  886.                 return(substr($value, 0, strlen('YYYY-MM-DD')));
  887.             case MDB_TYPE_TIME:
  888.                 return(substr($value, strlen('YYYY-MM-DD '), strlen('HH:MI:SS')));
  889.             default:
  890.                 return($this->_baseConvertResult($value, $type));
  891.         }
  892.     }
  893.  
  894.     // }}}
  895.     // {{{ numRows()
  896.  
  897.     /**
  898.      * returns the number of rows in a result object
  899.      * 
  900.      * @param ressource $result a valid result ressouce pointer
  901.      * @return mixed MDB_Error or the number of rows
  902.      * @access public 
  903.      */
  904.     function numRows($result)
  905.     {
  906.         $result_value = intval($result);
  907.         if (!isset($this->current_row[$result_value])) {
  908.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  909.                 'Number of rows: attemped to obtain the number of rows contained in an unknown query result'));
  910.         }
  911.         if (!isset($this->rows[$result_value])) {
  912.             if (MDB::isError($getcolumnnames = $this->getColumnNames($result))) {
  913.                 return($getcolumnnames);
  914.             }
  915.             if (isset($this->limits[$result_value])) {
  916.                 if (MDB::isError($skipfirstrow = $this->_skipLimitOffset($result))) {
  917.                     $this->rows[$result_value] = 0;
  918.                     return($skipfirstrow);
  919.                 }
  920.                 $limit = $this->limits[$result_value][1];
  921.             } else {
  922.                 $limit = 0;
  923.             }
  924.             if ($limit == 0 || $this->current_row[$result_value] + 1 < $limit) {
  925.                 if (isset($this->row_buffer[$result_value])) {
  926.                     $this->current_row[$result_value]++;
  927.                     $this->results[$result_value][$this->current_row[$result_value]] = $this->row_buffer[$result_value];
  928.                     unset($this->row_buffer[$result_value]);
  929.                 }
  930.                 while(($limit == 0 || $this->current_row[$result_value] + 1 < $limit)
  931.                     && @OCIFetchInto($result, $buffer, OCI_ASSOC+OCI_RETURN_NULLS)
  932.                 ) {
  933.                     $this->current_row[$result_value]++;
  934.                     $this->results[$result_value][$this->current_row[$result_value]] = $buffer;
  935.                 }
  936.             }
  937.             $this->rows[$result_value] = $this->current_row[$result_value] + 1;
  938.         }
  939.         return($this->rows[$result_value]);
  940.     }
  941.  
  942.     // }}}
  943.     // {{{ freeResult()
  944.  
  945.     /**
  946.      * Free the internal resources associated with $result.
  947.      * 
  948.      * @param  $result result identifier
  949.      * @return bool TRUE on success, FALSE if $result is invalid
  950.      * @access public 
  951.      */
  952.     function freeResult($result)
  953.     {
  954.         $result_value = intval($result);
  955.         if (!isset($this->current_row[$result_value])) {
  956.            return($this->raiseError(MDB_ERROR, NULL, NULL,
  957.                'Free result: attemped to free an unknown query result'));
  958.         }
  959.         if(isset($this->highest_fetched_row[$result])) {
  960.             unset($this->highest_fetched_row[$result]);
  961.         }
  962.         if(isset($this->row_buffer[$result_value])) {
  963.             unset($this->row_buffer[$result_value]);
  964.         }
  965.         if(isset($this->limits[$result_value])) {
  966.             unset($this->limits[$result_value]);
  967.         }
  968.         if(isset($this->current_row[$result_value])) {
  969.             unset($this->current_row[$result_value]);
  970.         }
  971.         if(isset($this->results[$result_value])) {
  972.             unset($this->results[$result_value]);
  973.         }
  974.         if(isset($this->columns[$result])) {
  975.             unset($this->columns[$result]);
  976.         }
  977.         if(isset($this->rows[$result_value])) {
  978.             unset($this->rows[$result_value]);
  979.         }
  980.         if(isset($this->result_types[$result])) {
  981.             unset($this->result_types[$result]);
  982.         }
  983.         return(@OCIFreeCursor($result));
  984.     }
  985.  
  986.     // }}}
  987.     // {{{ getTypeDeclaration()
  988.  
  989.     /**
  990.      * Obtain DBMS specific native datatype as a string
  991.      * 
  992.      * @param string $field associative array with the name of the properties
  993.      *        of the field being declared as array indexes. Currently, the types
  994.      *        of supported field properties are as follows:
  995.      * 
  996.      * @return string with the correct RDBMS native type
  997.      * @access public 
  998.      */
  999.     function getTypeDeclaration($field)
  1000.     {
  1001.         switch ($field['type']) {
  1002.             case 'integer':
  1003.                 return('INT');
  1004.             case 'text':
  1005.                 return('VARCHAR ('.(isset($field['length']) ? $field['length'] : (isset($this->options['DefaultTextFieldLength']) ? $this->options['DefaultTextFieldLength'] : 4000)).')');
  1006.             case 'boolean':
  1007.                 return('CHAR (1)');
  1008.             case 'date':
  1009.             case 'time':
  1010.             case 'timestamp':
  1011.                 return('DATE');
  1012.             case 'float':
  1013.                 return('NUMBER');
  1014.             case 'decimal':
  1015.                 return('NUMBER(*,'.$this->decimal_places.')');
  1016.         }
  1017.         return('');
  1018.     }
  1019.  
  1020.     // }}}
  1021.     // {{{ getIntegerDeclaration()
  1022.  
  1023.     /**
  1024.      * Obtain DBMS specific SQL code portion needed to declare an integer type
  1025.      * field to be used in statements like CREATE TABLE.
  1026.      * 
  1027.      * @param string $name name the field to be declared.
  1028.      * @param string $field associative array with the name of the properties
  1029.      *        of the field being declared as array indexes. Id
  1030.      * ently, the types
  1031.      *        of supported field properties are as follows:
  1032.      * 
  1033.      *        unsigned
  1034.      *            Boolean flag that indicates whether the field should be
  1035.      *            declared as unsigned integer if possible.
  1036.      * 
  1037.      *        default
  1038.      *            Integer value to be used as default for this field.
  1039.      * 
  1040.      *        notnull
  1041.      *            Boolean flag that indicates whether this field is constrained
  1042.      *            to not be set to NULL.
  1043.      * @return string DBMS specific SQL code portion that should be used to
  1044.      *        declare the specified field.
  1045.      * @access public 
  1046.      */
  1047.     function getIntegerDeclaration($name, $field)
  1048.     {
  1049.         if (isset($field['unsigned']))
  1050.             $this->warning = "unsigned integer field \"$name\" is being declared as signed integer";
  1051.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$field['default'] : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1052.     }
  1053.  
  1054.     // }}}
  1055.     // {{{ getTextDeclaration()
  1056.  
  1057.     /**
  1058.      * Obtain DBMS specific SQL code portion needed to declare an text type
  1059.      * field to be used in statements like CREATE TABLE.
  1060.      * 
  1061.      * @param string $name name the field to be declared.
  1062.      * @param string $field associative array with the name of the properties
  1063.      *        of the field being declared as array indexes. Currently, the types
  1064.      *        of supported field properties are as follows:
  1065.      * 
  1066.      *        length
  1067.      *            Integer value that determines the maximum length of the text
  1068.      *            field. If this argument is missing the field should be
  1069.      *            declared to have the longest length allowed by the DBMS.
  1070.      * 
  1071.      *        default
  1072.      *            Text value to be used as default for this field.
  1073.      * 
  1074.      *        notnull
  1075.      *            Boolean flag that indicates whether this field is constrained
  1076.      *            to not be set to NULL.
  1077.      * @return string DBMS specific SQL code portion that should be used to
  1078.      *        declare the specified field.
  1079.      * @access public 
  1080.      */
  1081.     function getTextDeclaration($name, $field)
  1082.     {
  1083.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getTextValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1084.     }
  1085.  
  1086.     // }}}
  1087.     // {{{ getClobDeclaration()
  1088.  
  1089.     /**
  1090.      * Obtain DBMS specific SQL code portion needed to declare an character
  1091.      * large object type field to be used in statements like CREATE TABLE.
  1092.      * 
  1093.      * @param string $name name the field to be declared.
  1094.      * @param string $field associative array with the name of the properties
  1095.      *        of the field being declared as array indexes. Currently, the types
  1096.      *        of supported field properties are as follows:
  1097.      * 
  1098.      *        length
  1099.      *            Integer value that determines the maximum length of the large
  1100.      *            object field. If this argument is missing the field should be
  1101.      *            declared to have the longest length allowed by the DBMS.
  1102.      * 
  1103.      *        notnull
  1104.      *            Boolean flag that indicates whether this field is constrained
  1105.      *            to not be set to NULL.
  1106.      * @return string DBMS specific SQL code portion that should be used to
  1107.      *        declare the specified field.
  1108.      * @access public 
  1109.      */
  1110.     function getClobDeclaration($name, $field)
  1111.     {
  1112.         return("$name CLOB".(isset($field['notnull']) ? ' NOT NULL' : ''));
  1113.     }
  1114.  
  1115.     // }}}
  1116.     // {{{ getBlobDeclaration()
  1117.  
  1118.     /**
  1119.      * Obtain DBMS specific SQL code portion needed to declare an binary large
  1120.      * object type field to be used in statements like CREATE TABLE.
  1121.      * 
  1122.      * @param string $name name the field to be declared.
  1123.      * @param string $field associative array with the name of the properties
  1124.      *        of the field being declared as array indexes. Currently, the types
  1125.      *        of supported field properties are as follows:
  1126.      * 
  1127.      *        length
  1128.      *            Integer value that determines the maximum length of the large
  1129.      *            object field. If this argument is missing the field should be
  1130.      *            declared to have the longest length allowed by the DBMS.
  1131.      * 
  1132.      *        notnull
  1133.      *            Boolean flag that indicates whether this field is constrained
  1134.      *            to not be set to NULL.
  1135.      * @return string DBMS specific SQL code portion that should be used to
  1136.      *        declare the specified field.
  1137.      * @access public 
  1138.      */
  1139.     function getBlobDeclaration($name, $field)
  1140.     {
  1141.         return("$name BLOB".(isset($field['notnull']) ? ' NOT NULL' : ''));
  1142.     }
  1143.  
  1144.     // }}}
  1145.     // {{{ getBooleanDeclaration()
  1146.  
  1147.     /**
  1148.      * Obtain DBMS specific SQL code portion needed to declare a boolean type
  1149.      * field to be used in statements like CREATE TABLE.
  1150.      * 
  1151.      * @param string $name name the field to be declared.
  1152.      * @param string $field associative array with the name of the properties
  1153.      *        of the field being declared as array indexes. Currently, the types
  1154.      *        of supported field properties are as follows:
  1155.      * 
  1156.      *        default
  1157.      *            Boolean value to be used as default for this field.
  1158.      * 
  1159.      *        notnullL
  1160.      *            Boolean flag that indicates whether this field is constrained
  1161.      *            to not be set to NULL.
  1162.      * @return string DBMS specific SQL code portion that should be used to
  1163.      *        declare the specified field.
  1164.      * @access public 
  1165.      */
  1166.     function getBooleanDeclaration($name, $field)
  1167.     {
  1168.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getBooleanValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1169.     }
  1170.  
  1171.     // }}}
  1172.     // {{{ getDateDeclaration()
  1173.  
  1174.     /**
  1175.      * Obtain DBMS specific SQL code portion needed to declare a date type
  1176.      * field to be used in statements like CREATE TABLE.
  1177.      * 
  1178.      * @param string $name name the field to be declared.
  1179.      * @param string $field associative array with the name of the properties
  1180.      *        of the field being declared as array indexes. Currently, the types
  1181.      *        of supported field properties are as follows:
  1182.      * 
  1183.      *        default
  1184.      *            Date value to be used as default for this field.
  1185.      * 
  1186.      *        notnull
  1187.      *            Boolean flag that indicates whether this field is constrained
  1188.      *            to not be set to NULL.
  1189.      * @return string DBMS specific SQL code portion that should be used to
  1190.      *        declare the specified field.
  1191.      * @access public 
  1192.      */
  1193.     function getDateDeclaration($name, $field)
  1194.     {
  1195.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getDateValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1196.     }
  1197.  
  1198.     // }}}
  1199.     // {{{ getTimestampDeclaration()
  1200.  
  1201.     /**
  1202.      * Obtain DBMS specific SQL code portion needed to declare a timestamp
  1203.      * field to be used in statements like CREATE TABLE.
  1204.      * 
  1205.      * @param string $name name the field to be declared.
  1206.      * @param string $field associative array with the name of the properties
  1207.      *        of the field being declared as array indexes. Currently, the types
  1208.      *        of supported field properties are as follows:
  1209.      * 
  1210.      *        default
  1211.      *            Timestamp value to be used as default for this field.
  1212.      * 
  1213.      *        notnull
  1214.      *            Boolean flag that indicates whether this field is constrained
  1215.      *            to not be set to NULL.
  1216.      * @return string DBMS specific SQL code portion that should be used to
  1217.      *        declare the specified field.
  1218.      * @access public 
  1219.      */
  1220.     function getTimestampDeclaration($name, $field)
  1221.     {
  1222.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getTimestampValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1223.     }
  1224.  
  1225.     // }}}
  1226.     // {{{ getTimeDeclaration()
  1227.  
  1228.     /**
  1229.      * Obtain DBMS specific SQL code portion needed to declare a time
  1230.      * field to be used in statements like CREATE TABLE.
  1231.      * 
  1232.      * @param string $name name the field to be declared.
  1233.      * @param string $field associative array with the name of the properties
  1234.      *        of the field being declared as array indexes. Currently, the types
  1235.      *        of supported field properties are as follows:
  1236.      * 
  1237.      *        default
  1238.      *            Time value to be used as default for this field.
  1239.      * 
  1240.      *        notnull
  1241.      *            Boolean flag that indicates whether this field is constrained
  1242.      *            to not be set to NULL.
  1243.      * @return string DBMS specific SQL code portion that should be used to
  1244.      *        declare the specified field.
  1245.      * @access public 
  1246.      */
  1247.     function getTimeDeclaration($name, $field)
  1248.     {
  1249.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getTimeValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1250.     }
  1251.  
  1252.     // }}}
  1253.     // {{{ getFloatDeclaration()
  1254.  
  1255.     /**
  1256.      * Obtain DBMS specific SQL code portion needed to declare a float type
  1257.      * field to be used in statements like CREATE TABLE.
  1258.      * 
  1259.      * @param string $name name the field to be declared.
  1260.      * @param string $field associative array with the name of the properties
  1261.      *        of the field being declared as array indexes. Currently, the types
  1262.      *        of supported field properties are as follows:
  1263.      * 
  1264.      *        default
  1265.      *            Float value to be used as default for this field.
  1266.      * 
  1267.      *        notnull
  1268.      *            Boolean flag that indicates whether this field is constrained
  1269.      *            to not be set to NULL.
  1270.      * @return string DBMS specific SQL code portion that should be used to
  1271.      *        declare the specified field.
  1272.      * @access public 
  1273.      */
  1274.     function getFloatDeclaration($name, $field)
  1275.     {
  1276.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getFloatValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1277.     }
  1278.  
  1279.     // }}}
  1280.     // {{{ getDecimalDeclaration()
  1281.  
  1282.     /**
  1283.      * Obtain DBMS specific SQL code portion needed to declare a decimal type
  1284.      * field to be used in statements like CREATE TABLE.
  1285.      * 
  1286.      * @param string $name name the field to be declared.
  1287.      * @param string $field associative array with the name of the properties
  1288.      *        of the field being declared as array indexes. Currently, the types
  1289.      *        of supported field properties are as follows:
  1290.      * 
  1291.      *        default
  1292.      *            Decimal value to be used as default for this field.
  1293.      * 
  1294.      *        notnull
  1295.      *            Boolean flag that indicates whether this field is constrained
  1296.      *            to not be set to NULL.
  1297.      * @return string DBMS specific SQL code portion that should be used to
  1298.      *        declare the specified field.
  1299.      * @access public 
  1300.      */
  1301.     function getDecimalDeclaration($name, $field)
  1302.     {
  1303.         return("$name ".$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getDecimalValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1304.     }
  1305.  
  1306.     // }}}
  1307.     // {{{ getClobValue()
  1308.  
  1309.     /**
  1310.      * Convert a text value into a DBMS specific format that is suitable to
  1311.      * compose query statements.
  1312.      * 
  1313.      * @param resource $prepared_query query handle from prepare()
  1314.      * @param  $parameter 
  1315.      * @param  $clob 
  1316.      * @return string text string that represents the given argument value in
  1317.      *        a DBMS specific format.
  1318.      * @access public 
  1319.      */
  1320.     function getClobValue($prepared_query, $parameter, $clob)
  1321.     {
  1322.         return('EMPTY_CLOB()');
  1323.     }
  1324.  
  1325.     // }}}
  1326.     // {{{ freeClobValue()
  1327.  
  1328.     /**
  1329.      * free a character large object
  1330.      * 
  1331.      * @param resource $prepared_query query handle from prepare()
  1332.      * @param string $blob 
  1333.      * @param string $value 
  1334.      * @access public 
  1335.      */
  1336.     function freeClobValue($prepared_query, $clob, &$value)
  1337.     {
  1338.         unset($value);
  1339.     }
  1340.  
  1341.     // }}}
  1342.     // {{{ getBlobValue()
  1343.  
  1344.     /**
  1345.      * Convert a text value into a DBMS specific format that is suitable to
  1346.      * compose query statements.
  1347.      * 
  1348.      * @param resource $prepared_query query handle from prepare()
  1349.      * @param  $parameter 
  1350.      * @param  $blob 
  1351.      * @return string text string that represents the given argument value in
  1352.      *        a DBMS specific format.
  1353.      * @access public 
  1354.      */
  1355.     function getBlobValue($prepared_query, $parameter, $blob)
  1356.     {
  1357.         return('EMPTY_BLOB()');
  1358.     }
  1359.  
  1360.     // }}}
  1361.     // {{{ freeBlobValue()
  1362.  
  1363.     /**
  1364.      * free a binary large object
  1365.      * 
  1366.      * @param resource $prepared_query query handle from prepare()
  1367.      * @param string $blob 
  1368.      * @param string $value 
  1369.      * @access public 
  1370.      */
  1371.     function freeBlobValue($prepared_query, $blob, &$value)
  1372.     {
  1373.         unset($value);
  1374.     }
  1375.  
  1376.     // }}}
  1377.     // {{{ getDateValue()
  1378.  
  1379.     /**
  1380.      * Convert a text value into a DBMS specific format that is suitable to
  1381.      * compose query statements.
  1382.      * 
  1383.      * @param string $value text string value that is intended to be converted.
  1384.      * @return string text string that represents the given argument value in
  1385.      *        a DBMS specific format.
  1386.      * @access public 
  1387.      */
  1388.     function getDateValue($value)
  1389.     {
  1390.         return(($value === NULL) ? 'NULL' : "TO_DATE('$value','YYYY-MM-DD')");
  1391.     }
  1392.  
  1393.     // }}}
  1394.     // {{{ getTimestampValue()
  1395.  
  1396.     /**
  1397.      * Convert a text value into a DBMS specific format that is suitable to
  1398.      * compose query statements.
  1399.      * 
  1400.      * @param string $value text string value that is intended to be converted.
  1401.      * @return string text string that represents the given argument value in
  1402.      *        a DBMS specific format.
  1403.      * @access public 
  1404.      */
  1405.     function getTimestampValue($value)
  1406.     {
  1407.         return(($value === NULL) ? 'NULL' : "TO_DATE('$value','YYYY-MM-DD HH24:MI:SS')");
  1408.     }
  1409.  
  1410.     // }}}
  1411.     // {{{ getTimeValue()
  1412.  
  1413.     /**
  1414.      * Convert a text value into a DBMS specific format that is suitable to
  1415.      *        compose query statements.
  1416.      * 
  1417.      * @param string $value text string value that is intended to be converted.
  1418.      * @return string text string that represents the given argument value in
  1419.      *        a DBMS specific format.
  1420.      * @access public 
  1421.      */
  1422.     function getTimeValue($value)
  1423.     {
  1424.         return(($value === NULL) ? 'NULL' : "TO_DATE('0001-01-01 $value','YYYY-MM-DD HH24:MI:SS')");
  1425.     }
  1426.  
  1427.     // }}}
  1428.     // {{{ getFloatValue()
  1429.  
  1430.     /**
  1431.      * Convert a text value into a DBMS specific format that is suitable to
  1432.      * compose query statements.
  1433.      * 
  1434.      * @param string $value text string value that is intended to be converted.
  1435.      * @return string text string that represents the given argument value in
  1436.      *        a DBMS specific format.
  1437.      * @access public 
  1438.      */
  1439.     function getFloatValue($value)
  1440.     {
  1441.         return(($value === NULL) ? 'NULL' : (float)$value);
  1442.     }
  1443.  
  1444.     // }}}
  1445.     // {{{ getDecimalValue()
  1446.  
  1447.     /**
  1448.      * Convert a text value into a DBMS specific format that is suitable to
  1449.      * compose query statements.
  1450.      * 
  1451.      * @param string $value text string value that is intended to be converted.
  1452.      * @return string text string that represents the given argument value in
  1453.      *        a DBMS specific format.
  1454.      * @access public 
  1455.      */
  1456.     function getDecimalValue($value)
  1457.     {
  1458.         return(($value === NULL) ? 'NULL' : $value);
  1459.     }
  1460.  
  1461.     // }}}
  1462.     // {{{ nextId()
  1463.  
  1464.     /**
  1465.      * returns the next free id of a sequence
  1466.      * 
  1467.      * @param string $seq_name name of the sequence
  1468.      * @param boolean $ondemand when TRUE the seqence is
  1469.      *                           automatic created, if it
  1470.      *                           not exists
  1471.      * @return mixed MDB_Error or id
  1472.      * @access public 
  1473.      */
  1474.     function nextId($seq_name, $ondemand = TRUE)
  1475.     {
  1476.         if (MDB::isError($connect = $this->connect())) {
  1477.             return($connect);
  1478.         }
  1479.         $sequence_name = $this->getSequenceName($seq_name);
  1480.         $this->expectError(MDB_ERROR_NOSUCHTABLE);
  1481.         $result = $this->_doQuery("SELECT $sequence_name.nextval FROM DUAL");
  1482.         $this->popExpect();
  1483.         if ($ondemand && MDB::isError($result) &&
  1484.             $result->getCode() == MDB_ERROR_NOSUCHTABLE)
  1485.         {
  1486.             // Since we are create the sequence on demand
  1487.             // we know the first id = 1 so initialize the
  1488.             // sequence at 2
  1489.             $result = $this->createSequence($seq_name, 2);
  1490.             if (MDB::isError($result)) {
  1491.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  1492.                     'Next ID: on demand sequence could not be created'));
  1493.             } else {
  1494.                 // First ID of a newly created sequence is 1
  1495.                 return(1);
  1496.             }
  1497.         }
  1498.         return($this->fetchOne($result));
  1499.     }
  1500.  
  1501.     // }}}
  1502.     // {{{ fetchInto()
  1503.  
  1504.     /**
  1505.      * Fetch a row and return data in an array.
  1506.      *
  1507.      * @param resource $result result identifier
  1508.      * @param int $fetchmode how the array data should be indexed
  1509.      * @param int $rownum the row number to fetch
  1510.      * @return mixed data array or NULL on success, a MDB error on failure
  1511.      * @access public
  1512.      */
  1513.     function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
  1514.     {
  1515.         $result_value = intval($result);
  1516.         if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
  1517.             $fetchmode = $this->fetchmode;
  1518.         }
  1519.         if (is_null($rownum)) {
  1520.             $rownum = $this->highest_fetched_row[$result_value] + 1;
  1521.         }
  1522.         if (!isset($this->results[$result_value][$rownum])) {
  1523.             if (isset($this->limits[$result_value])) {
  1524.                 if ($rownum >= $this->limits[$result_value][1]) {
  1525.                     return(NULL);
  1526.                 }
  1527.                 if (MDB::isError($this->_skipLimitOffset($result))) {
  1528.                     return(NULL);
  1529.                 }
  1530.             }
  1531.             if (isset($this->row_buffer[$result_value])) {
  1532.                 $this->current_row[$result_value]++;
  1533.                 $this->results[$result_value][$this->current_row[$result_value]] =
  1534.                     $this->row_buffer[$result_value];
  1535.                 unset($this->row_buffer[$result_value]);
  1536.             }
  1537.             while ($this->current_row[$result_value] < $rownum
  1538.                 && @OCIFetchInto($result, $buffer, OCI_ASSOC+OCI_RETURN_NULLS)
  1539.             ) {
  1540.                 $this->current_row[$result_value]++;
  1541.                 $this->results[$result_value][$this->current_row[$result_value]] = $buffer;
  1542.             }
  1543.             $this->highest_fetched_row[$result_value] =
  1544.                 max($this->highest_fetched_row[$result_value],
  1545.                     $this->current_row[$result_value]);
  1546.         }
  1547.         if (isset($this->results[$result_value][$rownum])) {
  1548.             $this->highest_fetched_row[$result_value] =
  1549.                 max($this->highest_fetched_row[$result_value], $rownum);
  1550.             $row = $this->results[$result_value][$rownum];
  1551.         } else {
  1552.             return(NULL);
  1553.         }
  1554.         if ($fetchmode == MDB_FETCHMODE_ASSOC) {
  1555.             if ( $this->options['optimize'] == 'portability') {
  1556.                 $row = array_change_key_case($row, CASE_LOWER);
  1557.             }
  1558.         } else {
  1559.             $row = array_values($row);
  1560.         }
  1561.         if (!$row) {
  1562.             if($this->options['autofree']) {
  1563.                 $this->freeResult($result);
  1564.             }
  1565.             return(NULL);
  1566.         }
  1567.         if (isset($this->result_types[$result])) {
  1568.             $row = $this->convertResultRow($result, $row);
  1569.         }
  1570.         return($row);
  1571.     }
  1572.  
  1573.     // }}}
  1574.     // {{{ nextResult()
  1575.  
  1576.     /**
  1577.      * Move the internal oracle result pointer to the next available result
  1578.      * Currently not supported
  1579.      * 
  1580.      * @param $result a oracle valid result resource
  1581.      * @return TRUE if a result is available otherwise return FALSE
  1582.      * @access public 
  1583.      */
  1584.     function nextResult($result)
  1585.     {
  1586.         return(FALSE);
  1587.     }
  1588.  
  1589.     // }}}
  1590.     // {{{ tableInfo()
  1591.  
  1592.     /**
  1593.      * returns meta data about the result set
  1594.      * 
  1595.      * @param resource $result result identifier
  1596.      * @param mixed $mode depends on implementation
  1597.      * @return array an nested array, or a MDB error
  1598.      * @access public 
  1599.      */
  1600.     function tableInfo($result, $mode = NULL)
  1601.     {
  1602.         $count = 0;
  1603.         $res = array();
  1604.         /**
  1605.          * depending on $mode, metadata returns the following values:
  1606.          * 
  1607.          * - mode is FALSE (default):
  1608.          * $res[]:
  1609.          *    [0]['table']       table name
  1610.          *    [0]['name']        field name
  1611.          *    [0]['type']        field type
  1612.          *    [0]['len']         field length
  1613.          *    [0]['nullable']    field can be NULL (boolean)
  1614.          *    [0]['format']      field precision if NUMBER
  1615.          *    [0]['default']     field default value
  1616.          * 
  1617.          * - mode is MDB_TABLEINFO_ORDER
  1618.          * $res[]:
  1619.          *    ['num_fields']     number of fields
  1620.          *    [0]['table']       table name
  1621.          *    [0]['name']        field name
  1622.          *    [0]['type']        field type
  1623.          *    [0]['len']         field length
  1624.          *    [0]['nullable']    field can be NULL (boolean)
  1625.          *    [0]['format']      field precision if NUMBER
  1626.          *    [0]['default']     field default value
  1627.          *    ['order'][field name] index of field named 'field name'
  1628.          *    The last one is used, if you have a field name, but no index.
  1629.          *    Test:  if (isset($result['order']['myfield'])) { ...
  1630.          * 
  1631.          * - mode is MDB_TABLEINFO_ORDERTABLE
  1632.          *     the same as above. but additionally
  1633.          *    ['ordertable'][table name][field name] index of field
  1634.          *       named 'field name'
  1635.          * 
  1636.          *       this is, because if you have fields from different
  1637.          *       tables with the same field name * they override each
  1638.          *       other with MDB_TABLEINFO_ORDER
  1639.          * 
  1640.          *       you can combine DB_TABLEINFO_ORDER and
  1641.          *       MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
  1642.          *       MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
  1643.          */ 
  1644.         // if $result is a string, we collect info for a table only
  1645.         if (is_string($result)) {
  1646.             $result = strtoupper($result);
  1647.             $q_fields = "select column_name, data_type, data_length, data_precision,
  1648.                      nullable, data_default from user_tab_columns
  1649.                      where table_name='$result' order by column_id";
  1650.             if (!$stmt = @OCIParse($this->connection, $q_fields)) {
  1651.                 return($this->oci8RaiseError());
  1652.             }
  1653.             if (!@OCIExecute($stmt, OCI_DEFAULT)) {
  1654.                 return($this->oci8RaiseError($stmt));
  1655.             } while (@OCIFetch($stmt)) {
  1656.                 $res[$count]['table'] = strtolower($result);
  1657.                 $res[$count]['name'] = strtolower(@OCIResult($stmt, 1));
  1658.                 $res[$count]['type'] = strtolower(@OCIResult($stmt, 2));
  1659.                 $res[$count]['len'] = @OCIResult($stmt, 3);
  1660.                 $res[$count]['format'] = @OCIResult($stmt, 4);
  1661.                 $res[$count]['nullable'] = (@OCIResult($stmt, 5) == 'Y') ? TRUE : FALSE;
  1662.                 $res[$count]['default'] = @OCIResult($stmt, 6);
  1663.                 if ($mode & MDB_TABLEINFO_ORDER) {
  1664.                     $res['order'][$res[$count]['name']] = $count;
  1665.                 }
  1666.                 if ($mode & MDB_TABLEINFO_ORDERTABLE) {
  1667.                     $res['ordertable'][$res[$count]['table']][$res[$count]['name']] = $count;
  1668.                 }
  1669.                 $count++;
  1670.             }
  1671.             $res['num_fields'] = $count;
  1672.             @OCIFreeStatement($stmt);
  1673.         } else { // else we want information about a resultset
  1674.             #if ($result === $this->last_stmt) {
  1675.                 $count = @OCINumCols($result);
  1676.                 for ($i = 0; $i < $count; $i++) {
  1677.                     $res[$i]['name'] = strtolower(@OCIColumnName($result, $i + 1));
  1678.                     $res[$i]['type'] = strtolower(@OCIColumnType($result, $i + 1));
  1679.                     $res[$i]['len'] = @OCIColumnSize($result, $i + 1);
  1680.  
  1681.                     $q_fields = "select table_name, data_precision, nullable, data_default from user_tab_columns where column_name='".$res[$i]['name']."'";
  1682.                     if (!$stmt = @OCIParse($this->connection, $q_fields)) {
  1683.                         return($this->oci8RaiseError());
  1684.                     }
  1685.                     if (!@OCIExecute($stmt, OCI_DEFAULT)) {
  1686.                         return($this->oci8RaiseError($stmt));
  1687.                     }
  1688.                     @OCIFetch($stmt);
  1689.                     $res[$i]['table'] = strtolower(@OCIResult($stmt, 1));
  1690.                     $res[$i]['format'] = @OCIResult($stmt, 2);
  1691.                     $res[$i]['nullable'] = (@OCIResult($stmt, 3) == 'Y') ? TRUE : FALSE;
  1692.                     $res[$i]['default'] = @OCIResult($stmt, 4);
  1693.                     @OCIFreeStatement($stmt);
  1694.  
  1695.                     if ($mode & MDB_TABLEINFO_ORDER) {
  1696.                         $res['order'][$res[$i]['name']] = $i;
  1697.                     }
  1698.                     if ($mode & MDB_TABLEINFO_ORDERTABLE) {
  1699.                         $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  1700.                     }
  1701.                 }
  1702.                 $res['num_fields'] = $count;
  1703.             #} else {
  1704.             #    return($this->raiseError(MDB_ERROR_NOT_CAPABLE));
  1705.             #}
  1706.         }
  1707.         return($res);
  1708.     }
  1709. }
  1710.  
  1711. ?>